<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>

<body>
  <script>
    "use strict"
    let message = "some string";
    console.log(typeof message); //string
    console.log(typeof (message)); //string
    console.log(typeof 99); //number
    console.log(typeof (null)) //object ,被认为是空对象的引用.

    let name;
    console.log(name); //underfined
    console.log(null == undefined) //true,undefined由null派生而来.

    let msg = "Hello Sloan";
    let msgAsBoolean = Boolean(msg);
    console.log(msgAsBoolean);

    //Number
    let intNum = 55;
    let octalNum1 = 0o70; //八进制56,严格模式下使用0o前缀
    console.log(octalNum1);
    let hexNum = 0xA; // 十六进制10,十六进制使用0x前缀.
    console.log(hexNum);

    let floatNum1 = 1.1;
    let floatNum2 = 0.1;
    let floatNum3 = 1.;
    console.log(floatNum3)//1,小数点后没有数字
    let floatNum4 = 10.0;
    console.log(floatNum4)//10 , 小数点后面是0
    let floatNum5 = 3.125e7; //31250000 , 3.125 * 10^7
    console.log(floatNum5)
    let floatNum6 = 0.0000007;
    console.log(floatNum6) //7e-7
    let result = Number.MAX_VALUE + Number.MAX_VALUE;
    console.log(isFinite(result)); //检测是否超出范围
    console.log(0 / 0); //NaN
    console.log(5 / 0) // Infinity.

    console.log(isNaN(NaN)); //true
    console.log(isNaN(10)); //false
    console.log(isNaN("8")); //false
    console.log(isNaN("blue")); // true
    console.log(isNaN(true)); //false

    //Number()
    console.log(Number(true)); //1
    console.log(Number(null)); //0
    console.log(Number(undefined)); // NaN
    console.log(Number("00011")); //11
    console.log(Number("")); //0

    //parseInt()
    console.log(parseInt("1234blue")); //1234,blue被忽略
    console.log(parseInt("")); //NaN
    console.log(parseInt("0xA")); //10
    console.log(parseInt("22.3")); //22
    console.log(parseInt("AF", 16)); // 175 ,第二个参数,传入进制数.
    console.log(parseInt("10", 2)); // 2 ,二进制.
    console.log(parseInt("10", 8)); //8,八进制.
    console.log(parseInt("10", 16)); // 16,十六进制.

    //parseFloat()
    console.log(parseFloat("1234blue")); //1234
    console.log(parseFloat("0xA")); //0
    console.log(parseFloat("22.5")) //22.5
    console.log(parseFloat("22.14.2")) //22.14
    console.log(parseFloat("0908.5")) //908.5
    console.log(parseFloat("3.125e6")) //3125000

    // string
    let text = "This is the letter sigma: \u03a3.";
    console.log(text);
    let age = 13;
    let ageAsString = age.toString();
    console.log(ageAsString); //"13"
    // 对于数值类型,可以接受参数表示进制数
    let num = 10;
    console.log(num.toString()) //"10"
    console.log(num.toString(2)) // "1010"
    console.log(num.toString(8)) //"12"
    console.log(num.toString(16)) //a

    let v1 = 10;
    let v2 = true;
    let v3 = null;
    let v4;
    console.log(String(v1), String(v2), String(v3), String(v4)); // 10,true,null,undefined.

    let Template = `first line
    second line`;
    console.log(Template);
    //字符串插值
    let val = 5;
    let exponent = 'second';
    let interpolated = `${val} to the ${exponent} power is ${val * val}`;
    console.log(interpolated);
    console.log(`Hello, ${`World`}!`);
    let foo = { toString: () => 'World' };
    console.log(`Hello,${foo}!`)
    function capitalize(word) {
      return `${word[0].toUpperCase()}${word.slice(1)}`;
    }
    console.log(`${capitalize('hello')},${capitalize('world')}!`)

    let str = '';
    function append() {
      str = `${str}abc`;
      console.log(str)
    }
    append();
    append();
    append();

    //标签函数.
    let a = 6;
    let b = 9;
    function simpleTag(strings, ...expressions) {
      console.log(strings);
      for (const expression of expressions) {
        console.log(expression);
      }
      return 'footbar';
    }
    let taggedResult = simpleTag`${a} + ${b} = ${a + b}`;
    console.log(taggedResult);
    //["","+","=",""]
    //6
    //9
    //15
    //footbar

    function zipTag(strings, ...expressions) {
      return strings[0] + expressions.map((e, i) => `${e}${strings[i + 1]}`).join('');
    }
    let untaggedResult = `${a}+${b}=${a + b}`;
    let taggedResult1 = zipTag`${a}+${b}=${a + b}`;
    console.log(untaggedResult);
    console.log(taggedResult1);

    //原始字符串
    console.log(`\u00A9`);
    console.log(String.raw`\u009A`); //获取原始字符串
    console.log(`firstline\nsecondline`);//换行
    console.log(String.raw`firstline\nsecondline`);//不换行.

    function printRaw(strings) {
      console.log('实际类型:');
      for (const string of strings) {
        console.log(string);
      }
      console.log('原始类型:')
      for (const rawString of strings.raw) {
        console.log(rawString)
      }
    }
    printRaw`\u00A9${'and'}\n`;
    //Symbol
    let sym = Symbol();
    console.log(typeof sym);// symbol

    let genericSymbol = Symbol();
    let otherGenericSymbol = Symbol();
    let fooSymbol = Symbol('foo');
    let otherFooSymbol = Symbol('foo');
    // Symbol是唯一的记号,是唯一且不可变的.
    console.log(genericSymbol == otherFooSymbol) //false
    console.log(fooSymbol == otherFooSymbol) //false
    console.log(genericSymbol);// Symbol()
    console.log(fooSymbol); // Symbol(foo)
    //原始类型的包装对象
    let myBoolean = new Boolean();
    console.log(typeof myBoolean); //object
    let myString = new String();
    console.log(typeof myString); //object
    let myNumber = new Number();
    console.log(typeof myNumber); //object
    // let mySymbol = new Symbol();  TypeError:Symbol is not a constructor
    // 把Symbol包装成对象
    let mySymbol = Symbol();
    let myWrappedSymbol = Object(mySymbol);
    console.log(typeof myWrappedSymbol) // object

    //Symbol.for(),全局符号注册表,必须用字符串键来创建.
    let localSymbol = Symbol('foo');
    let globalSymbol = Symbol.for('foo');
    let otherGlobalSymbal = Symbol.for('foo');
    console.log(localSymbol === globalSymbol) //false
    console.log(globalSymbol === otherGlobalSymbal) //true
    //Symbol.keyFor()
    let s = Symbol.for('foo');
    console.log(Symbol.keyFor(s)); //foo
    let s0 = Symbol('bar');
    console.log(Symbol.keyFor(s0)); //undefined;

    //使用符号作为属性
    let s1 = Symbol('foo'), s2 = Symbol('bar'), s3 = Symbol('baz'), s4 = Symbol('qux');
    let o = {
      [s1]: 'foo val'
    };
    console.log(o); //{Symbol(foo):foo val}
    //添加一个属性
    Object.defineProperty(o, s2, { value: 'bar val' });

    console.log(o); //{Symbol(foo): 'foo val', Symbol(bar): 'bar val'}
    //添加多个属性.
    Object.defineProperties(o, {
      [s3]: { value: 'baz val' },
      [s4]: { value: 'qux val' }
    })
    console.log(o);//{Symbol(foo): 'foo val', Symbol(bar): 'bar val', Symbol(baz): 'baz val', Symbol(qux): 'qux val'}

    let s5 = Symbol('foo'), s6 = Symbol('bar');
    let o2 = {
      [s5]: 'foo val',
      [s6]: 'bar val',
      baz: 'baz val',
      qux: 'qux val'
    };
    console.log(Object.getOwnPropertySymbols(o2))//[Symbol(foo), Symbol(bar)],符号属性.
    console.log(Object.getOwnPropertyNames(o2)) //['baz', 'qux'],常规属性.

    console.log(Object.getOwnPropertyDescriptors(o2))//四种类型都会包含的对象.
    console.log(Reflect.ownKeys(o2))//['baz', 'qux', Symbol(foo), Symbol(bar)],四种类型的键.
    // 查找符号属性为bar的属性键.
    let o3 = {
      [Symbol('foo')]: 'foo val',
      [Symbol('bar')]: 'bar val'
    };
    console.log(o3);
    let barSymbol = Object.getOwnPropertySymbols(o3).find((symbol) => symbol.toString().match(/bar/));
    console.log(barSymbol); //Symbol(bar)

    //Symbol.asyncIterator
    class Foo {
      async *[Symbol.asyncIterator]() { }
    }
    let f = new Foo();
    console.log(f[Symbol.asyncIterator]());//AsyncGenerator {<suspended>}

    class Emitter {
      constructor(max) {
        this.max = max;
        this.asyncIdx = 0;
      }
      async *[Symbol.asyncIterator]() {
        while (this.asyncIdx < this.max) {
          yield new Promise((resolve) => resolve(this.asyncIdx++));
        }
      }
    }

    async function asyncCount() {
      let emitter = new Emitter(5);
      for await (const x of emitter) {
        console.log(x);
      }
    }
    asyncCount(); //0 1 2 3 4

    //Symbol.hasInstance
    function Foo1() { }
    let f1 = new Foo1()
    console.log(Foo1[Symbol.hasInstance](f1)); //true
    class Bar1 { }
    let b1 = new Bar1();
    console.log(Bar1[Symbol.hasInstance](b1)); //true

    class Bar2 { }
    class Baz2 extends Bar2 {
      static [Symbol.hasInstance]() {
        return false;
      }
    }
    let b2 = new Baz2();
    console.log(Bar2[Symbol.hasInstance](b2)) //true
    console.log(b2 instanceof Bar2) // true
    console.log(Baz2[Symbol.hasInstance](b2)) //false
    console.log(b2 instanceof Baz2) // false


    //Symbol.isConcatSpreadable
    let inital = ['foo'];

    let array = ['bar'];
    console.log(array[Symbol.isConcatSpreadable]); //undefined
    console.log(inital.concat(array)); //['foo','bar']
    // 整个对象追加到数组末尾
    array[Symbol.isConcatSpreadable] = false;
    console.log(inital.concat(array)); // ['foo'.Array(1)]
    // 设为true,类数组对象会被打平.
    let arrayLikeObject = { length: 1, 0: 'baz' };
    console.log(arrayLikeObject[Symbol.isConcatSpreadable]) // undefined
    console.log(inital.concat(arrayLikeObject)) //['foo',{...}]
    arrayLikeObject[Symbol.isConcatSpreadable] = true;
    console.log(inital.concat(arrayLikeObject)) // ['foo','baz']
    //不是类数组的将被忽略
    let otherObject = new Set().add('qux');
    console.log(otherObject[Symbol.isConcatSpreadable]) //undefined
    console.log(inital.concat(otherObject)) // ['foo',Set(1)]
    otherObject[Symbol.isConcatSpreadable] = true;
    console.log(inital.concat(otherObject)) // ['foo']

    //Symbol.iterator
    class Foo2 {
      *[Symbol.iterator]() { }
    }
    let foo2 = new Foo2();
    console.log(foo2[Symbol.iterator]()); //Generator {<suspended>} (生成器函数)
    //隐式通过生成器函数返回
    class Emitter1 {
      constructor(max) {
        this.max = max;
        this.idx = 0;
      }
      *[Symbol.iterator]() {
        while (this.idx < this.max) {
          yield this.idx++;
        }
      }
    }
    function count1() {
      let emitter1 = new Emitter1(4);
      for (const x of emitter1) {
        console.log(x);
      }
    }
    count1();

    //Symbol.match
    console.log(RegExp.prototype[Symbol.match]);//ƒ [Symbol.match]() { [native code] }
    // 传入非正则会导致该值被转换为正则对象
    console.log('foobar'.match(/bar/));//['bar', index: 3, input: 'foobar', groups: undefined]
    //在static上重新定义Symbol.match
    class FooMatch {
      static [Symbol.match](target) {
        return target.includes('foo');
      }
    }
    console.log('foobar'.match(FooMatch));//true
    console.log('barbaz'.match(FooMatch));//false

    class StringMatch {
      constructor(str) {
        this.str = str;
      }
      [Symbol.match](target) {
        return target.includes(this.str)
      }
    }
    console.log('foobar'.match(new StringMatch('foo')));//true
    console.log('barbaz'.match(new StringMatch('qux')));//false

    //Symbol.replace
    console.log(RegExp.prototype[Symbol.replace]);//[Symbol.replace]() { [native code] }
    console.log('foobarbaz'.replace(/bar/, 'qux')); //fooquxbaz
    class FooReplace {
      static [Symbol.replace](target, replacement) {
        return target.split('foo').join(replacement);
      }
    }
    console.log('barfoobaz'.replace(FooReplace, 'qux'));//barquxbaz

    class StringReplace {
      constructor(str) {
        this.str = str;
      }
      [Symbol.replace](target, replacement) {
        return target.split(this.str).join(replacement);
      }
    }
    console.log('barfoobaz'.replace(new StringReplace('foo'), 'qux'));//barquxbaz

    //Symbol.search
    class FooSearch {
      static [Symbol.search](target) {
        return target.indexOf('foo');
      }
    }
    console.log('foobar'.search(FooSearch));//0
    console.log('barfoo'.search(FooSearch));//3
    console.log('barbza'.search(FooSearch));// -1(没有匹配的索引值)

    class StringSearch {
      constructor(str) {
        this.str = str;
      }
      [Symbol.search](target) {
        return target.indexOf(this.str);
      }
    }
    console.log('foobar'.search(new StringSearch('foo')));//0
    console.log('barfoo'.search(new StringSearch('foo')));//3
    console.log('barbaz'.search(new StringSearch('foo')));//-1 , 没有找到.

    // Symbol.species
    class Bar3 extends Array { }
    class Baz3 extends Array {
      static get [Symbol.species]() {
        return Array;
      }
    }
    let bar3 = new Bar3();
    console.log(bar3 instanceof Array);//true
    console.log(bar3 instanceof Bar3);//true
    bar3 = bar3.concat('bar3');
    console.log(bar3 instanceof Array);//true
    console.log(bar3 instanceof Bar3);//true

    let baz3 = new Baz3();
    console.log(baz3 instanceof Array);//true
    console.log(baz3 instanceof Baz3);//true
    baz3 = baz3.concat('baz3');
    console.log(baz3 instanceof Array); //true
    console.log(baz3 instanceof Baz3);//fasle ,baz3已经不是数组实例了.

    //Symbol.split
    console.log(RegExp.prototype[Symbol.split]);//ƒ [Symbol.split]() { [native code] }
    console.log('foobarbaz'.split(/bar/));//['foo', 'baz']
    class FooSplit {
      static [Symbol.split](target) {
        return target.split('foo');
      }
    }
    console.log('barfoobaz'.split(FooSplit));//['bar', 'baz']

    class StringSplit {
      constructor(str) {
        this.str = str;
      }
      [Symbol.split](target) {
        return target.split(this.str)
      }
    }
    console.log('barfoobaz'.split(new StringSplit('foo')));//['bar', 'baz']

    //Symbol.toPrimitive
    class Fo { }
    let fo = new Foo();
    console.log(3 + fo); //3[object Object]
    console.log(3 - fo); //NaN
    console.log(String(fo)); //[object Object]

    class Bar {
      constructor() {
        this[Symbol.toPrimitive] = function (hint) {
          switch (hint) {
            case 'number':
              return 3;
            case 'string':
              return 'string bar';
            case 'default':
            default:
              return 'default bar';
          }
        }
      }
    }
    let bar = new Bar();
    console.log(3 + bar); //3default bar
    console.log(3 - bar); //0
    console.log(String(bar)); //string bar

    //Symbol.toStringTag
    let set = new Set();
    console.log(set);//Set(0) {size: 0}
    console.log(set.toString());//[object Set]
    console.log(set[Symbol.toStringTag]);//Set

    class Foo3{};
    let foo3 = new Foo3();
    console.log(foo3);//Foo3 {}
    console.log(foo3.toString());//[object Object]
    console.log(foo3[Symbol.toStringTag]);//undefined

    class Bar4{
      constructor(){
        this[Symbol.toStringTag] = 'Bar4';
      }
    }
    let bar4 = new Bar4();
    console.log(bar4) //Bar4 {Symbol(Symbol.toStringTag): 'Bar4'}
    console.log(bar4.toString());//[object Bar4]
    console.log(bar4[Symbol.toStringTag]);//Bar4;转换为原始类型
    //Symbol.unscopables
    // with不可以在严格模式下使用.
    // let oo = {foo:'bar'};
    // with(oo){
    //   console.log(foo); //bar
    // }
    // oo[Symbol.unscopables] = {
    //   // 设置为true,阻止该属性出现在with环境绑定中.
    //   foo:true
    // };
    // with(oo){
    //   console.log(foo);//ReferenceError.
    // }

    //object类型
    // let o = new Object();

  </script>
</body>

</html>